#include "OneWire.h"
#include "DallasTemperature.h"

#include "tempControl.h"

/**
 * Construtor
 */
TempControl::TempControl(unsigned int pin, uint8_t *address, unsigned int resolution, unsigned int vectorsize, float hister) : _ramps(hister)
{
	// Define o pino onde esta o sensor
	_pin = pin;

	// Verifica ponteiro address
	if (address == NULL)
	{
		uint8_t addr[8] = {0,0,0,0,0,0,0};
		_address = addr;
	} else
	{
		// Define o endereco do sensor
		_address = address;
	}

	// Seta parametros
	_resolution = resolution;
	_vectorSize = vectorsize;

	// Inicializa vetor contendo valores lidos de temperatura
	_tempsVector = new float[_vectorSize];

	// Inicializa valores de temperatura media, maxima e minima
	_sumTemp = 0.0;
	_maxTemp = 0.0;
	_minTemp = 0.0;
	
	// Inicializa o indicador de primeira leitura
	_firstTemp = true;
	
	// Inicializa posicao inicial do vetor
	_vectorPos = 0;
}

/**
 * Destrutor
 */
TempControl::~TempControl()
{
	if (_sensor != NULL)
		delete _sensor;
	if (_dallas != NULL)
		delete _dallas;

	// Limpa vetor das temperaturas
	if (_tempsVector != NULL)
		delete[] _tempsVector;
}


/**
 * Setup
 */
void TempControl::Setup()
{
	// Inicializa a saida pra Serial
	Serial.begin(9600);
	// Inicializa ponteiro sensor
	_sensor = new OneWire(_pin);
	// Inicializa ponteiro dallas
	_dallas = new DallasTemperature(_sensor);

	// Inicializa parametros dos sensor de temperaturas
	_dallas->begin();
	_dallas->setResolution(_address, _resolution);

	// Inicializa o tempo inicial das rampas como sendo 0.0
	// Isso significa que ao fazer o controle de temperatura
	// O tempo de referencia de inicio das rampas sera 0.0
	// Como esta funcao eh chamada no setup, este tempo
	// Sera equivalente +- ao momento que a funcao Setup
	// eh chamada pelo Arduino
	Start();
}


/**
 * addressSet
 */
bool TempControl::addressSet( uint8_t *address)
{
	// Verifica ponteiro address
	if(address == NULL)
		return false;

	// Verifica validade do endereco
	if (_dallas->validAddress(address))
	{
		_address = address;
		return true;
	}

	return false;
}

/**
 * resolutionSet
 */
bool TempControl::resolutionSet (unsigned int resolution)
{
	_resolution = resolution;
	return	_dallas->setResolution(_address, _resolution);
}

/**
 * _temperatureGet
 */
float TempControl::_temperatureGet(unsigned int buffsize)
{
	char *temps = NULL;
	temps = new char[buffsize];
	temps[0] = '\0';

	// Pega a temperatura do sensor
	_dallas->requestTemperatures();
	float tempC = _dallas->getTempC(_address);
	// Verifica se ocorreu problema na leitura
	if (tempC == DEVICE_DISCONNECTED)
		return DEVICE_DISCONNECTED;

	// Verifica ponteiro de temperaturas
	if (_tempsVector == NULL)
		return DEVICE_DISCONNECTED;

	// Adiciona no vetor de temperaturas
	if(_firstTemp)
	{
		// Na primeira leitura, preenche vetor com mesmo valor
		for(int i=0; i<_vectorSize; i++)
		{
			_tempsVector[i] = tempC;
			// Faz a somatoria
			_sumTemp += tempC;
		}
		_firstTemp = false;
	}
	else
	{
		// Para leituras subsequentes, subtrai valor mais antigo
		_sumTemp -= _tempsVector[_vectorPos];
		// Atualiza com leitura nova
		_tempsVector[_vectorPos] = tempC;
		// Atualiza posicao e somatoria das temperaturas
		_vectorPos++;
		_sumTemp += tempC;
		if(_vectorPos == _vectorSize)
			_vectorPos = 0;
	}

	// Inicializa maximo e minimo
	_maxTemp = 1e-6;
	_minTemp = 1e6;
	// Percorre as temperaturas lidas e verifica max e min
	for(int i=0; i<_vectorSize; i++)
	{
		_maxTemp = (_tempsVector[i] > _maxTemp) ? _tempsVector[i] : _maxTemp;
		_minTemp = (_tempsVector[i] < _minTemp) ? _tempsVector[i] : _minTemp;
	}

	return tempC;
}

/**
 * serialPrint
 */
void TempControl::SerialPrint()
{
	// Imprime com Serial
	Serial.print("\nTemp Avg(C): ");
	Serial.print(_sumTemp/_vectorSize);
}

/**
 * addRamp
 */
bool TempControl::AddRamp(float tempi, float tempf, float duration)
{
	// Adiciona rampa no objeto de controle de rampas
	return	_ramps.AddRamp(tempi, tempf, duration);
}


/**
 * checkTemperature
 */
char TempControl::Control(float time)
{
	// Verifica se tempo esta valido
	if(time<0.0)
		return -1;

	// Faz a leitura do sensor de temperatura
	float tempC = _temperatureGet();

	// Verifica se valor do sensor eh valido
	if (tempC == DEVICE_DISCONNECTED)
	{
		Serial.print("Error: Sensor Disconnected\n" );
		return -1;
	}

	// Caso valor for valido, verifica nas rampas se eh para
	// aumentar ou diminuir temperatura
	// A temperatura passada eh a media das ultimas X leituras
	return _ramps.RiseTemp(time, _sumTemp/_vectorSize);
}
